package mqtt

import (
	"fmt"
	"log"
	"sync"
	"time"

	mqtt "github.com/eclipse/paho.mqtt.golang"
)

// MqttClient 结构体表示一个MQTT客户端实例
type MqttClient struct {
	client     mqtt.Client
	BrokerAddr string
	ClientID   string
	topics     map[string]byte
	mu         sync.RWMutex
}

// NewMqttClient 创建一个新的MQTT客户端实例
func NewMqttClient(brokerAddr string, clientID string) *MqttClient {
	return &MqttClient{
		BrokerAddr: brokerAddr,
		ClientID:   clientID,
		topics:     make(map[string]byte),
	}
}

// Connect 连接到MQTT代理服务器
func (c *MqttClient) Connect() error {
	// 创建MQTT客户端选项
	opts := mqtt.NewClientOptions()
	opts.AddBroker(c.BrokerAddr)
	opts.SetClientID(c.ClientID)
	opts.SetCleanSession(true)
	opts.SetAutoReconnect(true)
	opts.SetMaxReconnectInterval(5 * time.Second)
	opts.SetKeepAlive(60 * time.Second)
	opts.SetPingTimeout(10 * time.Second)

	// 设置连接回调
	opts.SetOnConnectHandler(func(client mqtt.Client) {
		log.Printf("Connected to MQTT broker: %s\n", c.BrokerAddr)
		// 重新订阅之前的主题
		c.mu.RLock()
		topics := make(map[string]byte)
		for topic, qos := range c.topics {
			topics[topic] = qos
		}
		c.mu.RUnlock()

		for topic, qos := range topics {
			if token := client.Subscribe(topic, qos, nil); token.Wait() && token.Error() != nil {
				log.Printf("Error resubscribing to topic %s: %v\n", topic, token.Error())
			}
		}
	})

	// 设置断开连接回调
	opts.SetConnectionLostHandler(func(client mqtt.Client, err error) {
		log.Printf("Connection lost: %v\n", err)
	})

	// 创建客户端
	c.client = mqtt.NewClient(opts)

	// 连接到服务器
	if token := c.client.Connect(); token.Wait() && token.Error() != nil {
		return fmt.Errorf("failed to connect to MQTT broker: %v", token.Error())
	}

	return nil
}

// Disconnect 断开与MQTT代理服务器的连接
func (c *MqttClient) Disconnect(quiesce uint) {
	if c.client != nil && c.client.IsConnected() {
		c.client.Disconnect(quiesce)
		log.Printf("Disconnected from MQTT broker\n")
	}
}

// Subscribe 订阅指定主题
func (c *MqttClient) Subscribe(topic string, qos byte, callback mqtt.MessageHandler) error {
	if c.client == nil || !c.client.IsConnected() {
		return fmt.Errorf("MQTT client not connected")
	}

	// 订阅主题
	if token := c.client.Subscribe(topic, qos, callback); token.Wait() && token.Error() != nil {
		return fmt.Errorf("failed to subscribe to topic %s: %v", topic, token.Error())
	}

	// 记录已订阅的主题
	c.mu.Lock()
	c.topics[topic] = qos
	c.mu.Unlock()

	log.Printf("Subscribed to topic: %s\n", topic)
	return nil
}

// Unsubscribe 取消订阅指定主题
func (c *MqttClient) Unsubscribe(topic string) error {
	if c.client == nil || !c.client.IsConnected() {
		return fmt.Errorf("MQTT client not connected")
	}

	// 取消订阅
	if token := c.client.Unsubscribe(topic); token.Wait() && token.Error() != nil {
		return fmt.Errorf("failed to unsubscribe from topic %s: %v", topic, token.Error())
	}

	// 从记录中删除
	c.mu.Lock()
	delete(c.topics, topic)
	c.mu.Unlock()

	log.Printf("Unsubscribed from topic: %s\n", topic)
	return nil
}

// Publish 发布消息到指定主题
func (c *MqttClient) Publish(topic string, qos byte, retained bool, payload interface{}) error {
	if c.client == nil || !c.client.IsConnected() {
		return fmt.Errorf("MQTT client not connected")
	}

	// 发布消息
	if token := c.client.Publish(topic, qos, retained, payload); token.Wait() && token.Error() != nil {
		return fmt.Errorf("failed to publish message to topic %s: %v", topic, token.Error())
	}

	log.Printf("Published message to topic %s\n", topic)
	return nil
}

// IsConnected 检查客户端是否已连接
func (c *MqttClient) IsConnected() bool {
	return c.client != nil && c.client.IsConnected()
}

// 创建全局的默认MQTT客户端
var defaultMqttClient *MqttClient
var mqttClientMutex sync.Mutex

// InitMqttClient 初始化全局MQTT客户端
func InitMqttClient(brokerAddr string, clientID string) error {
	mqttClientMutex.Lock()
	defer mqttClientMutex.Unlock()

	if defaultMqttClient == nil {
		defaultMqttClient = NewMqttClient(brokerAddr, clientID)
		return defaultMqttClient.Connect()
	}

	return nil
}

// GetMqttClient 获取全局MQTT客户端实例
func GetMqttClient() *MqttClient {
	mqttClientMutex.Lock()
	defer mqttClientMutex.Unlock()

	return defaultMqttClient
}

// CloseMqttClient 关闭全局MQTT客户端
func CloseMqttClient() {
	mqttClientMutex.Lock()
	defer mqttClientMutex.Unlock()

	if defaultMqttClient != nil {
		defaultMqttClient.Disconnect(250)
		defaultMqttClient = nil
	}
}
